import 'package:flutter/material.dart';

import '../../../utils/ex/string_ex.dart';
import '../ex/extension.dart';
import 'base/axis.dart';
import 'base/base_axis_chart.dart';

/// Y轴绘制器
///
/// Created by wanggaowan on 2023/9/15 14:52
class YAxisRender<C extends BaseAxisChartDelegate>
    with BaseYAxisRenderMixin<C> {
  YAxisRender({this.isDrawLabelByGridLine = true});

  /// 标签是否与网格线水平居中对齐，如果为false，则标签与网格中心点水平居中对齐
  final bool isDrawLabelByGridLine;

  @override
  void onDraw(C chart, Canvas canvas, Rect rect, double gridHeight,
      double labelMaxWidth, double xAxisRange) {
    var textPaint = TextPainter(textDirection: TextDirection.ltr);
    var paint = Paint();
    var path = Path();

    // 绘制轴线
    var xAxis = chart.xAxis;
    var yAxis = chart.yAxis;
    if (yAxis.lineWidth > 0) {
      paint.color = yAxis.lineColor;
      paint.strokeWidth = yAxis.lineWidth;
      paint.style = PaintingStyle.stroke;
      var x = rect.left + (rect.right - rect.left) / 2;
      path.moveTo(x, rect.top);
      path.lineTo(x, rect.bottom);
      if (yAxis.lineDash != null) {
        path = path.dashPath(yAxis.lineDash!);
      }
      canvas.drawPath(path, paint);
    }
    yAxis.$drawRect = rect;

    var labelList = yAxis.labelList;
    if (labelList == null || labelList.isEmpty) {
      return;
    }

    // 绘制轴线文本
    var yLineDrawBottom = rect.bottom - yAxis.startPadding;
    paint.color = yAxis.gridLineColor;
    paint.strokeWidth = yAxis.gridLineWidth;
    paint.style = PaintingStyle.stroke;
    int labelCount = labelList.length;
    for (int i = 0; i < labelCount; i++) {
      if (gridHeight > 0 && labelMaxWidth > 0) {
        if (isDrawLabelByGridLine) {
          drawLabelByGridLine(chart,canvas, textPaint, i, labelList, yAxis,
              gridHeight, rect.left, labelMaxWidth, yLineDrawBottom, false);
        } else {
          drawLabelByGridCenter(canvas, textPaint, i, labelList, yAxis,
              gridHeight, rect.left, labelMaxWidth, yLineDrawBottom, false);
        }
      }

      if ((i > 0 || yAxis.startPadding > 0) && yAxis.gridLineWidth > 0) {
        var stopX = yAxis.gridUseEndPadding
            ? rect.left + xAxisRange - xAxis.endPadding
            : rect.left + xAxisRange;
        if (yAxis.gridLineWidth > 0 && i == labelCount - 1) {
          // 由Y轴补全X轴Y轴交叉点缺失部分，如果yAxis.gridUseEndPadding为true，则Y轴结束点实际都在X轴最后一根轴线左边
          stopX += yAxis.gridLineWidth / 2;
        }

        path.reset();
        path.moveTo(
            yAxis.gridUseStartPadding
                ? rect.left + xAxis.startPadding + yAxis.lineWidth
                : rect.left + yAxis.lineWidth,
            yLineDrawBottom);
        path.lineTo(stopX, yLineDrawBottom);
        if (yAxis.gridLineDash != null) {
          path = path.dashPath(yAxis.gridLineDash!);
        }

        canvas.drawPath(path, paint);
      }

      yLineDrawBottom -= gridHeight;
    }

    if (!isDrawLabelByGridLine && yAxis.gridLineWidth > 0) {
      var stopX = yAxis.gridUseEndPadding
          ? rect.left + xAxisRange - xAxis.endPadding
          : rect.left + xAxisRange;
      if (yAxis.gridLineWidth > 0) {
        // 由Y轴补全X轴Y轴交叉点缺失部分，如果yAxis.gridUseEndPadding为true，则Y轴结束点实际都在X轴最后一根轴线左边
        stopX += yAxis.gridLineWidth / 2;
      }

      path.reset();
      path.moveTo(
          yAxis.gridUseStartPadding
              ? rect.left + xAxis.startPadding + yAxis.lineWidth
              : rect.left + yAxis.lineWidth,
          yLineDrawBottom);
      path.lineTo(stopX, yLineDrawBottom);
      if (yAxis.gridLineDash != null) {
        path = path.dashPath(yAxis.gridLineDash!);
      }

      canvas.drawPath(path, paint);
    }

    textPaint.dispose();
  }

  /// 绘制标签，内容与网格中点水平居中对齐
  @protected
  void drawLabelByGridCenter(
      Canvas canvas,
      TextPainter textPaint,
      int index,
      List<Label> labels,
      BaseYAxis yAxis,
      double gridHeight,
      double start,
      double labelMaxWidth,
      double yLineDrawBottom,
      bool isReverse) {
    // 绘制y轴文本
    var label = labels[index];
    var text = label.value;
    if (text.isNullOrEmpty || label.weight <= 0) {
      return;
    }

    var maxHeight = gridHeight * label.weight - yAxis.labelVerticalPadding;
    if (maxHeight <= 0) {
      // 文本绘制空间没有
      return;
    }

    var textStart = !isReverse
        ? start - yAxis.labelOffsetAxis - labelMaxWidth
        : start + yAxis.labelOffsetAxis;
    var textEnd =
        !isReverse ? start - yAxis.labelOffsetAxis : textStart + labelMaxWidth;

    var labelAlign = yAxis.labelAlign;
    var overAlign = yAxis.labelOverAlign;

    textPaint.text = TextSpan(
      text: text,
      style: TextStyle(
          fontSize: yAxis.labelTextSize,
          color: yAxis.labelColor,
          overflow: yAxis.textOverflow),
    );
    textPaint.maxLines = yAxis.labelMaxLines;
    textPaint.textAlign = yAxis.labelAlign;
    textPaint.ellipsis =
        yAxis.textOverflow == TextOverflow.ellipsis ? kEllipsis : null;

    textPaint.layout(minWidth: labelMaxWidth, maxWidth: labelMaxWidth);
    if (yAxis.labelMaxLines > 1 &&
        overAlign != labelAlign &&
        textPaint.computeLineMetrics().length > 1) {
      textPaint.textAlign = overAlign;
      textPaint.layout(minWidth: labelMaxWidth, maxWidth: labelMaxWidth);
    }

    // 相比gridHeight通过权重扩充/缩小的高度
    var overHeight = gridHeight * label.weight - gridHeight;
    var tempRect = Rect.fromLTRB(
        textStart,
        yLineDrawBottom -
            gridHeight -
            overHeight / 2 +
            yAxis.labelVerticalPadding / 2,
        textEnd,
        yLineDrawBottom + overHeight / 2 - yAxis.labelVerticalPadding / 2);
    var textHeight = textPaint.height;
    var topY = textHeight > tempRect.height
        ? tempRect.top
        : tempRect.top + tempRect.height / 2 - textHeight / 2;

    if (yAxis.textOverflow != TextOverflow.visible) {
      canvas.save();
      canvas.clipRect(tempRect);
    }

    textPaint.paint(canvas, Offset(tempRect.left, topY));

    if (yAxis.textOverflow != TextOverflow.visible) {
      canvas.restore();
    }
  }

  /// 绘制标签，内容与网格线水平居中对齐
  @protected
  void drawLabelByGridLine(
      C chart,
      Canvas canvas,
      TextPainter textPaint,
      int index,
      List<Label> labels,
      BaseYAxis yAxis,
      double gridHeight,
      double start,
      double labelMaxWidth,
      double yLineDrawBottom,
      bool isReverse) {
    // 绘制y轴文本
    var label = labels[index];
    var text = label.value;
    if (text.isNullOrEmpty || label.weight <= 0) {
      return;
    }

    var maxHeight = gridHeight * label.weight - yAxis.labelVerticalPadding;
    if (maxHeight <= 0) {
      // 文本绘制空间没有
      return;
    }

    var textStart = !isReverse
        ? start - yAxis.labelOffsetAxis - labelMaxWidth
        : start + yAxis.labelOffsetAxis;
    var textEnd =
        !isReverse ? start - yAxis.labelOffsetAxis : textStart + labelMaxWidth;

    // 起始标签与第二个标签之间的间隔由其它标签承担，因为起始标签高度只有网格高度的一半
    double offsetVag = yAxis.labelVerticalPadding /
        2 /
        (yAxis.labelCount <= 2 ? 1 : yAxis.labelCount - 1);

    var labelAlign = yAxis.labelAlign;
    var overAlign = yAxis.labelOverAlign;

    textPaint.text = TextSpan(
      text: text,
      style: TextStyle(
          fontSize: yAxis.labelTextSize,
          color: yAxis.labelColor,
          overflow: yAxis.textOverflow),
    );
    textPaint.maxLines = yAxis.labelMaxLines;
    textPaint.textAlign = yAxis.labelAlign;
    textPaint.ellipsis =
        yAxis.textOverflow == TextOverflow.ellipsis ? kEllipsis : null;

    textPaint.layout(minWidth: labelMaxWidth, maxWidth: labelMaxWidth);
    if (yAxis.labelMaxLines > 1 &&
        overAlign != labelAlign &&
        textPaint.computeLineMetrics().length > 1) {
      textPaint.textAlign = overAlign;
      textPaint.layout(minWidth: labelMaxWidth, maxWidth: labelMaxWidth);
    }

    var textHeight = textPaint.height;
    Rect tempRect;
    double topY;
    if (index == 0) {
      double bottom;
      double textHeightHalf = textHeight / 2;
      if (maxHeight / 2 + yAxis.startPadding <= textHeight) {
        bottom = yLineDrawBottom + yAxis.startPadding;
        topY = yLineDrawBottom - maxHeight / 2;
      } else if (maxHeight / 2 > textHeightHalf &&
          yAxis.startPadding > textHeightHalf) {
        topY = yLineDrawBottom - textHeightHalf;
        bottom = yLineDrawBottom + textHeightHalf;
      } else if (maxHeight / 2 <= yAxis.startPadding) {
        topY = yLineDrawBottom - maxHeight / 2;
        bottom = yLineDrawBottom + (textHeight - maxHeight / 2);
      } else {
        topY = yLineDrawBottom - (textHeight - yAxis.startPadding);
        bottom = yLineDrawBottom + yAxis.startPadding;
      }

      tempRect = Rect.fromLTRB(
          textStart, yLineDrawBottom - maxHeight / 2, textEnd, bottom);
    } else if (index != labels.length - 1) {
      var maxTextHeight = maxHeight - offsetVag;
      tempRect = Rect.fromLTRB(textStart, yLineDrawBottom - maxTextHeight / 2,
          textEnd, yLineDrawBottom + maxTextHeight / 2);
      if (textHeight > tempRect.height) {
        topY = tempRect.top;
      } else {
        topY = tempRect.top + tempRect.height / 2 - textHeight / 2;
      }
    } else {
      var paddingTop = chart.chartState?.widget.padding?.top??0;
      var maxTextHeight = maxHeight - offsetVag;
      var topHeight = yAxis.endPadding + paddingTop;
      double textHeightHalf = textHeight / 2;

      tempRect = Rect.fromLTRB(textStart, yLineDrawBottom - topHeight,
          textEnd, yLineDrawBottom + maxTextHeight / 2);

      if (maxHeight / 2 + topHeight <= textHeight) {
        topY = yLineDrawBottom - topHeight;
      } else if (maxHeight / 2 > textHeightHalf &&
          topHeight > textHeightHalf) {
        topY = yLineDrawBottom - textHeightHalf;
      } else if (maxHeight / 2 <= topHeight) {
        topY = yLineDrawBottom + maxHeight / 2 - textHeight;
      } else {
        topY = yLineDrawBottom - topHeight;
      }
    }

    if (yAxis.textOverflow != TextOverflow.visible) {
      canvas.save();
      canvas.clipRect(tempRect);
    }

    textPaint.paint(canvas, Offset(tempRect.left, topY));

    if (yAxis.textOverflow != TextOverflow.visible) {
      canvas.restore();
    }
  }
}
